/*
 *
 * fork_process.cpp
 *	 A simple wrapper on top of fork(). This does not handle the
 *	 EXEC_BACKEND case; it might be extended to do so, but it would be
 *	 considerably more complex.
 *
 * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
 * Copyright (c) 1996-2012, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  src/gausskernel/process/postmaster/fork_process.cpp
 */
#include "postgres.h"
#include "knl/knl_variable.h"
#include "postmaster/fork_process.h"
#include "miscadmin.h"

#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>

#ifndef WIN32
/*
 * Wrapper for fork(). Return values are the same as those for fork():
 * -1 if the fork failed, 0 in the child process, and the PID of the
 * child in the parent process.
 */
pid_t fork_process(void)
{
    pid_t result;

#ifdef LINUX_PROFILE
    struct itimerval prof_itimer;
#endif

    /*
     * Flush stdio channels just before fork, to avoid double-output problems.
     * Ideally we'd use fflush(NULL) here, but there are still a few non-ANSI
     * stdio libraries out there (like SunOS 4.1.x) that coredump if we do.
     * Presently stdout and stderr are the only stdio output channels used by
     * the postmaster, so fflush'ing them should be sufficient.
     */
    fflush(stdout);
    fflush(stderr);

#ifdef LINUX_PROFILE

    /*
     * Linux's fork() resets the profiling timer in the child process. If we
     * want to profile child processes then we need to save and restore the
     * timer setting.  This is a waste of time if not profiling, however, so
     * only do it if commanded by specific -DLINUX_PROFILE switch.
     */
    getitimer(ITIMER_PROF, &prof_itimer);
#endif

    result = fork();

    if (result == 0) {
        /* fork succeeded, in child */
#ifdef LINUX_PROFILE
        setitimer(ITIMER_PROF, &prof_itimer, NULL);
#endif

        /*
         * By default, Linux tends to kill the postmaster in out-of-memory
         * situations, because it blames the postmaster for the sum of child
         * process sizes *including shared memory*.  (This is unbelievably
         * stupid, but the kernel hackers seem uninterested in improving it.)
         * Therefore it's often a good idea to protect the postmaster by
         * setting its oom_score_adj value negative (which has to be done in a
         * root-owned startup script). If you just do that much, all child
         * processes will also be protected against OOM kill, which might not
         * be desirable.  You can then choose to build with
         * LINUX_OOM_SCORE_ADJ #defined to 0, or to some other value that you
         * want child processes to adopt here.
         */
#ifdef LINUX_OOM_SCORE_ADJ
        {
            /*
             * Use open() not stdio, to ensure we control the open flags. Some
             * Linux security environments reject anything but O_WRONLY.
             */
            int fd = open("/proc/self/oom_score_adj", O_WRONLY, 0);

            /* We ignore all errors */
            if (fd >= 0) {
                char buf[16];
                int rc;

                errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_SCORE_ADJ);
                securec_check_intval(rcs, );

                rc = write(fd, buf, strlen(buf));
                (void)rc;
                close(fd);
            }
        }
#endif /* LINUX_OOM_SCORE_ADJ */

        /*
         * Older Linux kernels have oom_adj not oom_score_adj.  This works
         * similarly except with a different scale of adjustment values.
         * If it's necessary to build openGauss to work with either API,
         * you can define both LINUX_OOM_SCORE_ADJ and LINUX_OOM_ADJ.
         */
#ifdef LINUX_OOM_ADJ
        {
            /*
             * Use open() not stdio, to ensure we control the open flags. Some
             * Linux security environments reject anything but O_WRONLY.
             */
            int fd = open("/proc/self/oom_adj", O_WRONLY, 0);

            /* We ignore all errors */
            if (fd >= 0) {
                char buf[16];
                int rc;

                errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_ADJ);
                securec_check_intval(rcs, );

                rc = write(fd, buf, strlen(buf));
                (void)rc;
                close(fd);
            }
        }
#endif /* LINUX_OOM_ADJ */

        /* Binding static TLS variables for current thread */
        EarlyBindingTLSVariables();
    }

    return result;
}

#endif /* ! WIN32 */
